package ffmpeg;


import enums.FFmpegCodeEnum;
import exception.FFmpegException;
import file.FileOperating;
import file.Pid;
import listen.ProcessListen;
import org.apache.commons.lang3.StringUtils;
import utils.SnowflakeUuid;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;


public class FFmpeg {

    public List<String> getArgs() {
        return args;
    }

    public void setArgs(List<String> args) {
        this.args = args;
    }

    public Process getProcess() {
        return process;
    }

    public void setProcess(Process process) {
        this.process = process;
    }

    public String getRtspUrl() {
        return rtspUrl;
    }

    public void setRtspUrl(String rtspUrl) {
        this.rtspUrl = rtspUrl;
    }

    public String getRtmpUrl() {
        return rtmpUrl;
    }

    public void setRtmpUrl(String rtmpUrl) {
        this.rtmpUrl = rtmpUrl;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getHttpUrl() {
        return httpUrl;
    }

    public void setHttpUrl(String httpUrl) {
        this.httpUrl = httpUrl;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    private enum Status {
        WAITING,
        RUNNING,
        FINISHED,
        FAILED
    }

    private List<String> args;

    private Process process;

    private String rtspUrl;

    private String rtmpUrl;

    private int id;

    private String pid;

    private String httpUrl;

    private Status status = Status.WAITING;

    private FFmpeg() {}

    /**
     *
     * @param ffmpegPath:
     * @param ffprobePath:
     * @param rtspUrl:
     * @param ip: 127.0.0.1
     * @param rtmpPort: 1935
     * @param httpPort: 8000
     * @param timeMillis: 单位：毫秒 连接rtsp流地址最长时间
     */
    public FFmpeg(String ffmpegPath, String ffprobePath, String rtspUrl, String ip, Integer rtmpPort, Integer httpPort, long timeMillis) {
        // 校验rtsp地址
        if (!rtspUrl.startsWith("rtsp://")) {
            throw new FFmpegException(FFmpegCodeEnum.RTSP_ERROR);
        }
        // ffmpeg指令是否可用
        ffmpegVersion(ffmpegPath);
        String uuid = SnowflakeUuid.uuid();
        this.id = uuid.hashCode();
        // 探测流地址是否可用
        FFprobe fFprobe = new FFprobe(ffprobePath, rtspUrl, timeMillis);
        fFprobe.isNormal();
        String vcodec = fFprobe.getVcodec();
        String vcodecArgs;
        if (vcodec == null || vcodec.equals("h264")) {
            vcodecArgs = "copy";
        } else {
            vcodecArgs = "h264";
        }
        this.rtspUrl = rtspUrl;
        this.rtmpUrl = "rtmp://"+ ip.trim() + ":" + rtmpPort +"/live/" + uuid;
        this.httpUrl = "http://" + ip.trim() + ":" + httpPort + "/live?port=" + rtmpPort + "&app=live&stream=" + uuid;
        args = Args.camera_rtsp_rtmp(ffmpegPath, this.rtspUrl, vcodecArgs, this.rtmpUrl);
    }

    public void run() {
        status = Status.RUNNING;
        try {
            Process process = runProcess();
            this.process = process;
            status = Status.FINISHED;
            FFmpeg fFmpeg = new FFmpeg();
            fFmpeg.setProcess(process);
            fFmpeg.setRtspUrl(rtspUrl);
            fFmpeg.setRtmpUrl(rtmpUrl);
            fFmpeg.setArgs(args);
            fFmpeg.setStatus(status);
            fFmpeg.setId(id);
            fFmpeg.setPid(pid);
            fFmpeg.setHttpUrl(httpUrl);
            ProcessMap.processManagerHashMap.put(id, fFmpeg);
            new ProcessListen().isAlive(fFmpeg);
        } catch (IOException e) {
            e.printStackTrace();
            status = Status.FAILED;
            throw new FFmpegException(FFmpegCodeEnum.FFMPEG_ERROR);
        }
    }

    private Process runProcess() throws IOException {
        ProcessBuilder builder = new ProcessBuilder(args);
        builder.redirectErrorStream(true);
        Process process = builder.start();

        // 消费标准输出流，否则ffmpeg推流会在6分钟左右阻塞 - https://blog.csdn.net/weixin_29713403/article/details/113082236
        new Thread(() -> {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line;
                while (StringUtils.isNotEmpty(line = reader.readLine())) {
                }
            } catch (IOException e) {
            }
        }).start();

        String filePath = "/tmp/ffmpeg-push/pids.txt";
        pid = Pid.pid(rtmpUrl);
        FileOperating.writeFile(true, filePath, pid);
        return process;
    }

    private void ffmpegVersion(String ffmpegPath) {

        ProcessBuilder builder = new ProcessBuilder(Args.version(ffmpegPath));
        builder.redirectErrorStream(true);
        try {
            Process process = builder.start();
            process.getOutputStream().close();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while (StringUtils.isNotEmpty(line = reader.readLine())) {
                stringBuilder.append(line).append("\n");
            }
        } catch (IOException e) {
            throw new FFmpegException(FFmpegCodeEnum.FFMPEG_NONE);
        }
    }

}

